import { BoxGeometry, Mesh, MeshBasicMaterial, Raycaster, Vector3 } from "three";
import { App, Point } from "../../Engine";
import { Line2 } from "three/examples/jsm/lines/Line2";
// import { DragControls } from "three/examples/jsm/controls/DragControls";
// import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { LineGeometry } from "three/examples/jsm/lines/LineGeometry";
import { LineMaterial } from "three/examples/jsm/lines/LineMaterial";
import Const from "../../application/constants/Const";
/**
 * 通视分析
 */
export default class InvisibilityAnalysis {
    private app: App;
    private line: Line2;
    private data: {viewerPoint: Point, targetPoint: Point, lineWidth?: number};
    private isOpenAnalysis: boolean = false;

    /**
     * 通视分析
     * @example let viewerPoint = new GE.Point(13377184.375437068, 3540339.2581293588, 0, '3857');
        let targetPoint = new GE.Point(13377952.49603797, 3539759.3270052206, 10, '3857');
        let data = {viewerPoint: viewerPoint, targetPoint: targetPoint, lineWidth: 1};
        let invisibilityAnalysis = new GE.InvisibilityAnalysis();
        invisibilityAnalysis.openAnalysis(data); //开启通视分析
        //invisibilityAnalysis.closeAnalysis(); //关闭通视分析
     */
    public constructor(){
        this.app = App.getInstance();
    }

    /**
     * 开启通视分析
     * @param data 通视分析需要用到的参数
     * @param data.viewerPoint 通视分析的起点
     * @param data.targetPoint 通视分析的终点
     * @param data.lineWidth 通视分析线条的宽度
     * @returns 打开分析是否成功。如果通视分析的起点或者终点在可视区域外，分析失败
     * @example let viewerPoint = new GE.Point(13377184.375437068, 3540339.2581293588, 0, '3857');
        let targetPoint = new GE.Point(13377952.49603797, 3539759.3270052206, 10, '3857');
        let data = {viewerPoint: viewerPoint, targetPoint: targetPoint, lineWidth: 1};
        invisibilityAnalysis.openAnalysis(data);
     */
    public openAnalysis(data: {viewerPoint: Point, targetPoint: Point, lineWidth?: number}){
        if(this.isOpenAnalysis){
            return true;
        }
        this.data = data;
        this.data.viewerPoint.toEPSGWeb();
        this.data.targetPoint.toEPSGWeb();

        let isOutside = false;
        let pointArray = new Array<Point>();
        pointArray.push(this.data.viewerPoint);
        pointArray.push(this.data.targetPoint);
        let pointRange = this.app.map.getMapBound();
        for(let i=0; i<pointArray.length; i++){
            let element = pointArray[i].toEPSGWeb();
            if (element.x < pointRange.minPointX || element.x > pointRange.maxPointX || element.y < pointRange.minPointY || element.y > pointRange.maxPointY) {
                isOutside = true;
                break;
            }
        }
        if(isOutside){
            return false;
        }

        this.isOpenAnalysis = true;
        let ray: Raycaster = this.getRay();
        let lineData = this.getLineData(ray);
        this.generateLine(lineData);
        return true
    }

    /**
     * 关闭通视分析
     * @example invisibilityAnalysis.closeAnalysis();
     */
    public closeAnalysis(){
        this.app.remove(this.line);
        this.line = null;
        this.data = null;
        this.isOpenAnalysis = false;
    }

    private getRay() {
        let directorVector = this.data.targetPoint.clone().sub(this.data.viewerPoint);
        let distance = directorVector.length();
        directorVector = directorVector.normalize();
        let ray: Raycaster = new Raycaster(this.data.viewerPoint, directorVector, 0, distance);
        ray.camera = this.app.three.camera;
        return ray;
    }

    private getLineData(ray: Raycaster){
        let scene = this.app.three.scene;
        let objList = scene.children;
        let intersectPoints = ray.intersectObjects(objList, true);
        let positions;
        let colors;
        if (intersectPoints && intersectPoints.length > 0) {
            let firstIntersect = intersectPoints[0];
            let intersectPoint = new Vector3(firstIntersect.point.x, firstIntersect.point.y, firstIntersect.point.z);
            positions = [
                0, 0, 0,
                intersectPoint.x - this.data.viewerPoint.x, intersectPoint.y - this.data.viewerPoint.y, intersectPoint.z - this.data.viewerPoint.z,

                //这里设置两个重复的点，是为了避免直线的颜色插值
                intersectPoint.x - this.data.viewerPoint.x, intersectPoint.y - this.data.viewerPoint.y, intersectPoint.z - this.data.viewerPoint.z,
                intersectPoint.x - this.data.viewerPoint.x, intersectPoint.y - this.data.viewerPoint.y, intersectPoint.z - this.data.viewerPoint.z,

                this.data.targetPoint.x - this.data.viewerPoint.x, this.data.targetPoint.y - this.data.viewerPoint.y, this.data.targetPoint.z - this.data.viewerPoint.z
            ];
            colors = [
                0, 1, 0,
                0, 1, 0,

                //解决颜色插值
                0, 1, 0,
                1, 0, 0,

                1, 0, 0
            ];
        } else {
            positions = [
                0, 0, 0,
                this.data.targetPoint.x - this.data.viewerPoint.x, this.data.targetPoint.y - this.data.viewerPoint.y, this.data.targetPoint.z - this.data.viewerPoint.z
            ];
            colors = [
                0, 1, 0,
                0, 1, 0
            ];
        }

        return {positions: positions, colors: colors};
    }

    private generateLine(lineData){
        if(this.line){
            return;
        }
        let geometry: LineGeometry = new LineGeometry();

        let lineMaterial: LineMaterial = new LineMaterial( {
			linewidth: this.data.lineWidth || 1, // in world units with size attenuation, pixels otherwise
			vertexColors: true,
			dashed: false,
			alphaToCoverage: true,
		});
        lineMaterial.fog = true;
        this.line = new Line2( geometry, lineMaterial );
        lineMaterial.resolution.set( window.innerWidth, window.innerHeight ); // resolution of the viewport
		this.line.scale.set( 1, 1, 1 );
        this.app.add( this.line );
        this.line.position.copy(this.data.viewerPoint);
        geometry.setPositions( lineData.positions );
        geometry.setColors( lineData.colors );
        this.line.computeLineDistances();
    }
}